﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk.Workflow;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Crm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

/**
 * Copyright 2011 by Alberto Gemin
 * agemin@hotmail.com
 * Version 1.7.0.1
 * 13 Dec 2011
 **/
namespace Gemina.CRM2011.WF.LdgManager
{
    [AgCodeActivity(ActivityNameRetrieveView, AgViewsUtils.GroupName)]
    public class AgRetrieveView : AgCodeActivityBase
    {

        #region Constants

        private const string ActivityNameRetrieveView = "Retrieve View";
        private const string Param1InputSavedQuery = "System View";
        private const string Param2InputUserQueryName = "Personal View Name";
        private const string Param3InputUserQueryOwner = "Personal View Owner";
        private const string Param4InputUserViewReturnedType = "Personal View Returned Entity";
        private const string Param5InputUserQueryType = "Personal View Query Type";
        private const string Param1OutputViewCount = "Count of Views";
        private const string Param2OutputViewId = "Output View Id";
        private const string Param3OutputEntityLogicalName = "Returned Entity";
        private const string Param4OutputFetchXml = "Fetch XML";
        private const string Param5OutputLayoutXml = "Layout XML";
        private const string Param6OutputQueryType = "Query Type";
        private const string Param7OutputViewDefinition = "View Definition";
        private const string MsgBothNotNull = "{0} and {1} cannot be both populated.";
        private const string MsgTooMuchInfo = "{0} can be populated only when {1} is populated.";
        private const string MsgOwner = "{0} cannot be null when retrieving {1}.";
        private const string MsgBothNull = "{0} and {1} cannot be both null.";

        #endregion

        #region Properties

        public EntityReference ValidatedSavedQuery { get; set; }
        public EntityReference ValidatedUserQueryOwner { get; set; }
        public string ValidatedEntityLogicalName { get; set; }
        public string ValidatedUserQueryName { get; set; }
        public bool ValidatedIsSavedQuery { get; set; }
        public int? ValidatedQueryType { get; set; }

        #endregion

        #region Workflow Input Parameters

        /// <summary>
        /// Input parameter
        /// </summary>
        [Input(Param1InputSavedQuery)]
        [ReferenceTarget(AgViewsUtils.EntityNameSavedQuery)]
        public InArgument<EntityReference> InputSavedQuery { get; set; }

        /// <summary>
        /// Input parameter
        /// </summary>
        [Input(Param2InputUserQueryName)]
        public InArgument<String> InputUserQueryName { get; set; }

        /// <summary>
        /// Input parameter
        /// </summary>
        [Input(Param3InputUserQueryOwner)]
        [ReferenceTarget(AgViewsUtils.EntityNameSystemUser)]
        public InArgument<EntityReference> InputUserQueryOwner { get; set; }

        /// <summary>
        /// Input parameter
        /// </summary>
        [Input(Param4InputUserViewReturnedType)]
        public InArgument<String> InputEntityLogicalName { get; set; }

        /// <summary>
        /// Input parameter
        /// </summary>
        [Input(Param5InputUserQueryType)]
        public InArgument<String> InputQueryType { get; set; }

        #endregion

        #region Workflow Output Parameters

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param1OutputViewCount)]
        public OutArgument<int> OutputViewCount { get; set; }

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param2OutputViewId)]
        public OutArgument<String> OutputViewId { get; set; }

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param3OutputEntityLogicalName)]
        public OutArgument<string> OutputEntityLogicalName { get; set; }

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param4OutputFetchXml)]
        public OutArgument<String> OutputFetchXml { get; set; }

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param5OutputLayoutXml)]
        public OutArgument<String> OutputLayoutXml { get; set; }

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param6OutputQueryType)]
        public OutArgument<int> OutputQueryType { get; set; }

        /// <summary>
        /// Output parameter
        /// </summary>
        [Output(Param7OutputViewDefinition)]
        public OutArgument<String> OutputViewDefinition { get; set; }

        #endregion

        #region CodeActivity

        override protected bool ExecuteBody(CodeActivityContext executionContext)
        {
            this.CheckArguments(executionContext);

            if (this.ValidatedIsSavedQuery)
            {
                this.RetrieveSavedQuery(ValidatedSavedQuery, executionContext);
            }
            else
            {
                this.RetrieveUserQuery(
                    this.ValidatedUserQueryName,
                    this.ValidatedUserQueryOwner,
                    string.IsNullOrEmpty(this.ValidatedEntityLogicalName) ? null : this.ValidatedEntityLogicalName,
                    this.ValidatedQueryType,
                    executionContext);
            }

            return true;
        }

        #endregion

        #region Local Methods

        private void CheckArguments(CodeActivityContext executionContext)
        {
            string queryType = this.InputQueryType.Get(executionContext);

            this.ValidatedSavedQuery = this.InputSavedQuery.Get(executionContext);
            this.ValidatedUserQueryOwner = this.InputUserQueryOwner.Get(executionContext);
            this.ValidatedEntityLogicalName = this.InputEntityLogicalName.Get(executionContext);
            this.ValidatedUserQueryName = this.InputUserQueryName.Get(executionContext);
            this.ValidatedIsSavedQuery = (this.ValidatedSavedQuery != null);
            this.ValidatedQueryType = null;

            if (this.ValidatedIsSavedQuery)
            {
                if (!string.IsNullOrEmpty(this.ValidatedUserQueryName))
                {
                    throw new AgErrorMessage(string.Format(MsgBothNotNull, Param1InputSavedQuery, Param2InputUserQueryName));
                }
                if (this.ValidatedUserQueryOwner != null)
                {
                    throw new AgErrorMessage(string.Format(MsgTooMuchInfo, Param3InputUserQueryOwner, Param2InputUserQueryName));
                }
                if (!string.IsNullOrEmpty(queryType))
                {
                    throw new AgErrorMessage(string.Format(MsgTooMuchInfo, Param5InputUserQueryType, Param2InputUserQueryName));
                }
                if (!string.IsNullOrEmpty(this.ValidatedEntityLogicalName))
                {
                    throw new AgErrorMessage(string.Format(MsgTooMuchInfo, Param4InputUserViewReturnedType, Param2InputUserQueryName));
                }
            }
            else
            {
                if (string.IsNullOrEmpty(this.ValidatedUserQueryName))
                {
                    throw new AgErrorMessage(string.Format(MsgBothNull, Param1InputSavedQuery, Param2InputUserQueryName));
                }
                if (this.ValidatedUserQueryOwner == null)
                {
                    throw new AgErrorMessage(string.Format(MsgOwner, Param3InputUserQueryOwner, Param2InputUserQueryName));
                }

                this.ValidatedQueryType = AgViewsUtils.GetQueryType(queryType, false, Param5InputUserQueryType, Param2InputUserQueryName);
            }
        }

        private void RetrieveSavedQuery(EntityReference inputSavedQuery, CodeActivityContext executionContext)
        {
            Entity response = (Entity)this.GetService(executionContext).Retrieve(AgViewsUtils.EntityNameSavedQuery, inputSavedQuery.Id, new ColumnSet(true));

            this.OutputViewCount.Set(executionContext, 1);
            this.SetOutputValues(response, executionContext);
        }

        private void SetOutputValues(Entity response, CodeActivityContext executionContext)
        {
            this.OutputViewId.Set(executionContext, response.Id.ToString());
            if (response.Attributes.Contains(AgViewsUtils.FieldFetchXml))
            {
                this.OutputFetchXml.Set(executionContext, response.Attributes[AgViewsUtils.FieldFetchXml]);
            }
            if (response.Attributes.Contains(AgViewsUtils.FieldLayoutXml))
            {
                this.OutputLayoutXml.Set(executionContext, response.Attributes[AgViewsUtils.FieldLayoutXml]);
            }
            if (response.Attributes.Contains(AgViewsUtils.FieldQueryType))
            {
                this.OutputQueryType.Set(executionContext, response.Attributes[AgViewsUtils.FieldQueryType]);
            }
            if (response.Attributes.Contains(AgViewsUtils.FieldReturnedTypeCode))
            {
                this.OutputEntityLogicalName.Set(executionContext, response.Attributes[AgViewsUtils.FieldReturnedTypeCode]);
            }
            this.OutputViewDefinition.Set(executionContext, this.Serialize(response));
        }

        private void RetrieveUserQuery(string userQueryName, EntityReference userQueryOwner, string userQueryTargetEntity, int? userQueryType, CodeActivityContext executionContext)
        {
            QueryExpression query = new QueryExpression
            {
                EntityName = AgViewsUtils.EntityNameUserQuery,
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression
                {
                    Conditions = 
                    {
                        new ConditionExpression
                        {
                            AttributeName = AgViewsUtils.FieldName,
                            Operator = ConditionOperator.Equal,
                            Values = { userQueryName}
                        }
                    }
                }
            };

            if (userQueryType != null)
            {
                query.Criteria.AddCondition(new ConditionExpression
                {
                    AttributeName = AgViewsUtils.FieldQueryType,
                    Operator = ConditionOperator.Equal,
                    Values = { userQueryType }
                });
            }

            if (userQueryTargetEntity != null)
            {
                query.Criteria.AddCondition(new ConditionExpression
                {
                    AttributeName = AgViewsUtils.FieldReturnedTypeCode,
                    Operator = ConditionOperator.Equal,
                    Values = { userQueryTargetEntity }
                });
            }

            IOrganizationService svc;
            if (userQueryOwner != null)
            {
                Guid user = userQueryOwner.Id;
                svc = this.GetService(executionContext, user);
            }
            else
            {
                svc = this.GetService(executionContext);
            }

            DataCollection<Entity> responses = svc.RetrieveMultiple(query).Entities;
            this.OutputViewCount.Set(executionContext, responses.Count);

            if (responses.Count == 1)
            {
                Entity response = responses[0];

                this.SetOutputValues(response, executionContext);
            }
        }

        #endregion

    }
}
